package com.jiunuan.live.msg.provider.service.impl;

import com.cloopen.rest.sdk.BodyType;
import com.cloopen.rest.sdk.CCPRestSmsSDK;
import com.jiunuan.live.common.interfaces.utils.DESUtils;
import com.jiunuan.live.framework.redis.starter.key.MsgProviderCacheKeyBuilder;
import com.jiunuan.live.msg.dto.MsgCheckDTO;
import com.jiunuan.live.msg.enums.MsgResultEnum;
import com.jiunuan.live.msg.provider.config.ApplicationProperties;
import com.jiunuan.live.msg.provider.config.SmsTemplateIDEnum;
import com.jiunuan.live.msg.provider.dao.mapper.SmsMapper;
import com.jiunuan.live.msg.provider.dao.po.SmsPO;
import com.jiunuan.live.msg.provider.config.ThreadPoolManger;
import com.jiunuan.live.msg.provider.service.ISmsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;

import java.util.HashMap;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class SmsServiceImpl implements ISmsService {
    @Resource
    private RedisTemplate<String,Object> redisTemplate;
    @Resource
    private MsgProviderCacheKeyBuilder cacheKeyBuilder;
    @Resource
    private SmsMapper smsMapper;
    @Resource
    private ApplicationProperties applicationProperties;
    @Value("${spring.cloud.nacos.config.namespace}")
    private String namespace;
    @Override
    public MsgResultEnum sendLoginCode(String phone) {
        if(StringUtils.isEmpty(phone)){
            return MsgResultEnum.MSG_PARAM_ERROR;
        }
        // 生成验证码4-6， 有效期30-60s，同一手机无法重复发送，redis存储手机号
        String codeCacheKey = cacheKeyBuilder.buildSmsLoginCodeKey(phone);
        if(redisTemplate.hasKey(codeCacheKey)){
            log.info("该手机号短信发送过于频繁，phone:{}", phone);
            return MsgResultEnum.SEND_FAIL;
        }
        // 生成验证码
        int code = randomCode(applicationProperties.getCodeLength());
        redisTemplate.opsForValue().set(codeCacheKey,code,1, TimeUnit.MINUTES);

        // 发送短信
        ThreadPoolManger.commonAsyncPool.execute(()->{
            boolean sendStatus = sendSmsCCP(phone,code);
            // 插入验证码发送记录
            if(sendStatus){
                insertOne(phone,code);
            }
        });
        return MsgResultEnum.SEND_SUCCESS;
    }
    // 根据参数要求随机生成指定长度的验证码
    private int randomCode(int length){
        int minValue = (int) Math.pow(10, length - 1);
        int maxValue = (int) Math.pow(10, length);
        // 范围内生成一个指定长度的随机数
        return RandomUtils.nextInt(minValue, maxValue);
    }
    // 验证验证码长度是否正确
    private boolean checkCodeLength(Integer code){
        return code.toString().length() == applicationProperties.getCodeLength();
    }
    @Override
    public MsgCheckDTO checkLoginCode(String phone, Integer code) {
        // 参数校验
        if(StringUtils.isEmpty(phone) || code == null || !checkCodeLength(code) ){
            return new MsgCheckDTO(false, MsgResultEnum.MSG_PARAM_ERROR.getDesc());
        }
        // redis校验验证码
        String codeCacheKey = cacheKeyBuilder.buildSmsLoginCodeKey(phone);
        Integer cacheCode = (Integer) redisTemplate.opsForValue().get(codeCacheKey);
        if(cacheCode == null || !checkCodeLength(cacheCode)){
            return new MsgCheckDTO(false, MsgResultEnum.CAPTCHA_HAS_EXPIRED.getDesc());
        }
        if(cacheCode.equals(code)){
            // 删除验证码
            redisTemplate.delete(codeCacheKey);
            return new MsgCheckDTO(true, MsgResultEnum.VERIFICATION_SUCCESS.getDesc());
        }
        return new MsgCheckDTO(false, MsgResultEnum.VERIFICATION_FAILED.getDesc());
    }

    @Override
    public void insertOne(String phone, Integer code) {
        SmsPO smsPO = new SmsPO();
        smsPO.setPhone(DESUtils.encrypt(phone));
        smsPO.setCode(code);
        smsMapper.insert(smsPO);
    }

    private boolean sendSmsCCP(String phone, Integer code) {
        log.info("phone is {},code is {}", phone, code);
        //测试环境就不发送短信了，节省话费
        if(applicationProperties.getIsDev()){
            return true;
        }
        try {
            // 生产环境请求地址：app.cloopen.com
            String serverIp = applicationProperties.getSmsServerIp();
            //请求端口
            String serverPort = String.valueOf(applicationProperties.getPort());
            //主账号,登陆云通讯网站后,可在控制台首页看到开发者主账号ACCOUNT SID和主账号令牌AUTH TOKEN
            String accountSId = applicationProperties.getAccountSId();
            String accountToken = applicationProperties.getAccountToken();
            //请使用管理控制台中已创建应用的APPID
            String appId = applicationProperties.getAppId();
            CCPRestSmsSDK sdk = new CCPRestSmsSDK();
            sdk.init(serverIp, serverPort);
            sdk.setAccount(accountSId, accountToken);
            sdk.setAppId(appId);
            sdk.setBodyType(BodyType.Type_JSON);
            // todo 测试账号，所有短信都会往这里发送，正式上线后才能发送给其他用户
            String to = applicationProperties.getTestPhone();
            String templateId = SmsTemplateIDEnum.SMS_LOGIN_CODE_TEMPLATE.getTemplateId();
            //测试开发支持的文案如下：【云通讯】您的验证码是{1}，请于{2}分钟内正确输入。其中{1}和{2}为短信模板参数。
            String[] datas = {String.valueOf(code), "1"};
            //可选 扩展码，四位数字 0~9999
            String subAppend = "1234";
            String reqId = UUID.randomUUID().toString();
            //可选 第三方自定义消息id，最大支持32位英文数字，同账号下同一自然天内不允许重复
            HashMap<String, Object> result = sdk.sendTemplateSMS(to, templateId, datas, subAppend, reqId);
            if ("000000".equals(result.get("statusCode"))) {
                //正常返回输出data包体信息（map）
                HashMap<String, Object> data = (HashMap<String, Object>) result.get("data");
                Set<String> keySet = data.keySet();
                for (String key : keySet) {
                    Object object = data.get(key);
                    log.info("[sendSmsToCCP] key is {},object is {}", key, object);
                }
            } else {
                //异常返回输出错误码和错误信息
                log.error("错误码:{},错误信息:{}", result.get("statusCode"), result.get("statusMsg"));
                return false;
            }
            return true;
        } catch (Exception e) {
            log.error("[sendSmsToCCP] error is ", e);
            throw new RuntimeException(e);
        } finally {
            return false;
        }
    }
}
